じぶんが使いたいので “割り勘アプリ” をつくった - Saboten
旅の精算とかで使えるアプリつくりました。N番煎じ。
https://scrapbox.io/files/675edf35c254078928252109.jpeg
https://scrapbox.io/files/65cf2f62a21ea10024496826.PNG https://scrapbox.io/files/65cf2f58ba0d100025a30cd8.PNG https://scrapbox.io/files/65cf2f7319cd89002e48a0fc.PNG https://scrapbox.io/files/65cf2f8299f8aa00242b3f84.PNG
使っていただけると嬉しいです。
しかしながら、幾度も煎じられているように、探してみると多種多様な割り勘アプリがありまして、意外にもデファクトっぽいものは無いなあと思います。逆にいえば何でも良いのかもしれないのですが、そんな中でも意志を持ってる人がまたひとつ煎じているのかもしれないですね。
そもそもお金にならないので本気なプロダクトは出てこない感じはあるのですが、自分としてはオープンソースにしてみんなで少しづつ良くしていけるようなプロダクトとしてひとつ世において置くというのは一理あるかなと思いまして作った次第です。
コンセプトというと大袈裟なんですが、個性を出さないように質素な作りにする、というのを常に考えていました。
ふつうに使えることは、まず大事。ツールですから、必要な時に必要な使命を果たせればそれで十分。
単純で、色のない心地よさ。うん、使えるね、くらいの気持ちで使ってもらえるのがちょうど良いなと思います。
あとはすこし楽しさの要素をスパイスとして加えてるとこはあるかなと思います。アイコンとか出してみたり。
フィードバックやコメントなどありましたら、Xでお気軽にリプライなど送ってもらって大丈夫です。励みになります。
またプロダクトを良くしたいと感じてくれた方も、どうぞ連絡ください。
ソースコードは公開されてますので、中身が気になる方もどうぞ。
---
(追記 2024/12/20)
考えていることを記事に書きました。
---
アプリ自体について特筆して紹介する部分はないので、ここからは技術的な部分の紹介をします。
構成は、
フロントエンドは vite でビルドした1枚のReactアプリケーション
API は Hono RPC 、エンドポイントは Clouflare Workers に置いてある
DB は Cloudflare D1に置いてある。Hono でリクエストを受けて drizzle でクエリする 細かいところ。スタイリングは tailwind。アイコンセットに Lucide を使ってる。 技術的な部分もわりと素朴です。Next.js とかは使わず、古典的なSPAになりました。
REST APIではなくHono RPCを使っている点はすこし新しいかなとは思います。レスポンスの型定義を楽に整合性保てるのでとてもよいです。もともとはtRPCだったんですけど、Honoの薄さで十分ですね。
フロントエンドはミューテーションが多いアプリケーションなので、Optimistic Update を重視してクライアント側にDBのドキュメントに匹敵するレベルのステートツリーを管理しています。それ故にSSRは向かないですね。ページの概念も薄いのでディレクトリベースのルーティングのメリットもそこまで享受できませんし、アイランドアーキテクチャなど不要です。
Reactはこれといったこだわりはないですが、結果としてvaulが使えたのでよかったなという感じです。vaulはとてもよかった。styleが付いていないというのをメインのコンセプトに置きつつ、ジェスチャやアニメーションの丁寧な実装が魅力なんですけど、そのstyleが付いていないという境目の絶妙さまるでネイティブ実装かと思うような設計が美しい。とても共感できる設計で、勉強にもなります。
あとはCloudflare Workers, D1を使っている点。割り勘アプリという特性上、旅の間に使われるという想定をしていたので、世界中からサクサク使えたらいいなあというコンセプトに対して最適でした。しかしまだベータなのでリードレプリカは無いんですけどね(なので今は強整合です)。あと不安定ですが、これはいつか改善すると思うのでOK。2GBの上限もいまのところの設計であれば問題ないです。
D1には drizzle でクエリしています。もともとは Prisma & ふつうのDBインスタンス を使っていたんですが、エッジからどこかのDBオリジンサーバーにクエリを発行するときには Prisma Data Proxy というコネクションプール兼クエリエンジンを使う必要があってこれが結構めんどくさいです。
drizzleの話をすると、だいぶスキーマ定義のクセが強いです。TypeScriptでプログラマティックに記述するので、ビルド不要で型定義がアップデートされるという点は最初は良いなと思ったのですが、どのみちDB側のスキーマ変更をpushするのでそのタイミングで型とDBの実態が整合したほうが自然な気がするなあという感想でした。
自由度が高いので、一部のスキーマを変数に入れたりとかは便利だったりはするんですけど、型定義だけではスキーマを検証しきれていないです。なので結局のところ検証しやすいschema.prismaのようなスキーマ言語があったほうが親切なんだろうなと感じました。しかしこれもトレードオフだとは思っていて、フォーマッタやバリデータ、シンタックスハイライトなどたくさんのツールキットが不要になるのは単純でよい設計ではあると思います。逆にPrismaはフレームワークとしてすごく頑張ってたんだなあと感心しました。
クエリについてはSQLライクに記述できる点はすごく良いです。そしてちゃんとクエリビルダーも持っていて場合によって使い分けることができる点もちゃんとニーズを汲み取ってて良い。しかもその棲み分けがちゃんと型レベルでどこまで頑張るかの線引が絶妙で良い設計だなと思いました。というのも、SQLライクに書きたいときというのは正直生クエリを直で書くくらいの最適化重視の気持ちになっているので、クエリを書くのを邪魔されたくない。だけれどクエリ結果だけはちゃんとデシリアライズされてスキーマに沿った型がついてくれたら嬉しいなー、という欲張りにちゃんと答えてくれる。クエリを組み立てるときの自由度は犠牲にしないように型はゆるくなってて頑張ってないし、推論できないところはアノテーションを与えられる。この塩梅がすごく良い。逆にdb.queryのクエリビルダーを使うときには、アグリゲーションがめんどいなあという気持ちのとき。このケースはRelation情報を元にして型の補完を効かせつつ便利に結果を組み立ててくれる。Prismaでは全部後者しかなかったので、型の整合性はちゃんとしていたけれど、そのかわりに書きたいクエリが書けなくなってしまっていた。かゆいところを良い感じにかいてくれてるのが気持ちよくてdrizzle信者になりました。
あとはtailwind、これはまあ前々から良いです。シンタックスは好きではないのだけど、ビルドがシンプルでアプリケーションコードのtsxなどをパースして何らかのtransformしたりとかを一切しない点が非常に良い。purgeの単純さが最高。これはNext.jsとかのほうがよりメリットを享受しやすいのだけど、そのシンプルさが好きで使ってます。シンタックスには難アリなので同じコンセプトのなにかがあったら普通に移行するかなと思います。
アイコンセットのLucide、とても可愛くてよい。初見は可愛すぎるかなあと思ってたんですけど、おもったより自然でした。動物とか楽しげなシンボルが多いのも良かった。lucide-react は tree shaking も素直に効いて良い感じです。
プロジェクトの型まわりについては結構気にして設計しました。tsconfigをブラウザ,Cloudflare Workersなど各種ランタイムごとに分離して配置して、lib,typesなどで事故らないようにしてます。
だらだらと長くなってしまったんですが、いろいろ技術の素振りになっていたところもあって、楽しく作れました。そこそこ面白い技術構成にはなってるかなと思いますので、もし興味あるところがありましたら一緒にお話ししましょう!